package com.geek.project.geekredisstarter.service.impl;

import com.geek.project.geekredisstarter.props.RedisProperties;
import com.geek.project.geekredisstarter.service.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.*;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;


/**
 * RedisServiceImpl redis
 *
 * @author geek
 */
@Slf4j
public class RedisServiceImpl implements RedisService {

    private RedisTemplate<String, Object> redisTemplate;
    private RedisProperties redisProperties;

    public RedisServiceImpl(RedisTemplate redisTemplate, RedisProperties redisProperties) {
        this.redisTemplate = redisTemplate;
        this.redisProperties = redisProperties;
    }

    public RedisTemplate<String, Object> getRedisTemplate() {
		return redisTemplate;
	}

	/**
     * 指定缓存失效时间
     *
     * @param key  键
     * @param time 时间(秒)
     * @return
     */
	@Override
    public boolean expire(String key, long time) {
    	Boolean result = redisTemplate.expire(getKey(key), time, TimeUnit.SECONDS);
    	closeConnection();
    	return result;
    }

	/**
     * 根据key 获取过期时间
     *
     * @param key 键 不能为null
     * @return 时间(秒) 返回0代表为永久有效
     */
	@Override
    public Long getExpire(String key) {
        Long expire = redisTemplate.getExpire(getKey(key), TimeUnit.SECONDS);
        closeConnection();
        return expire;
    }

    /**
     * 判断key是否存在
     *
     * @param key 键
     * @return true 存在  false不存在
     */
    @Override
    public boolean hasKey(String key) {
    	Boolean result = redisTemplate.hasKey(getKey(key));
    	closeConnection();
    	return result;
    }

    /**
     * 删除缓存
     *
     * @param key 可以传一个值 或多个
     * @return
     */
    @Override
	public Boolean del(String... key) {
    	Boolean result = false;
    	if(key!=null && key.length==1) {
    		result = redisTemplate.delete(getKey(key[0]));
    	}else {
    		for(int index=0;index<key.length;index++) {
        		key[index] = getKey(key[index]);
        	}
    		Long results = redisTemplate.delete(CollectionUtils.arrayToList(key));
    		log.info("key共{}个，删除成功{}个",key.length,results);
    	}
    	closeConnection();
        return result;
    }

    @Override
    public void batchDelete(String pattern) {
		Set<String> keys = redisTemplate.keys(this.getKey(pattern));
		Long results = redisTemplate.delete(keys);
		log.info("key共{}个，删除成功{}个",keys.size(),results);
	}
    
    // ============================String=============================

    /**
     * 普通缓存获取
     *
     * @param key 键
     * @return 值
     */
    @Override
    public Object get(String key) {
    	Object obj = redisTemplate.opsForValue().get(getKey(key));
    	closeConnection();
    	return obj;
    }

    /**
     * 普通缓存放入
     *
     * @param key   键
     * @param value 值
     * @return true成功 false失败
     */
    @Override
    public boolean set(String key, Object value) {
        try {
            redisTemplate.opsForValue().set(getKey(key), value);
            return true;
        } catch (Exception e) {
            log.error("redis error: ", e);
            return false;
        } finally {
        	closeConnection();
        }
    }

    /**
     * 普通缓存放入并设置时间
     *
     * @param key   键
     * @param value 值
     * @param time  时间(秒) time要大于0 如果time小于等于0 将设置无限期
     * @return true成功 false 失败
     */
    @Override
    public boolean set(String key, Object value, long time) {
        try {
            if (time > 0) {
                redisTemplate.opsForValue().set(getKey(key), value, time, TimeUnit.SECONDS);
            } else {
                return this.set(key, value);
            }
            return true;
        } catch (Exception e) {
            log.error("redis error: ", e);
            return false;
        } finally {
        	closeConnection();
        }
    }

    /**
     * 递增
     *
     * @param key 键
     * @param delta  要增加几(大于0)
     * @return
     */
    @Override
    public Long increment(String key, long delta) {
        if (delta < 0) {
            throw new IllegalArgumentException("递增因子必须大于0");
        }
        Long result = redisTemplate.opsForValue().increment(getKey(key), delta);
        closeConnection();
        return result;
    }

    /**
     * 递增
     *
     * @param key 键
     * @param delta  要增加几(大于0)
     * @return
     */
    @Override
    public Long increment(String key, long delta,Long expire) {
        if (delta < 0) {
            throw new IllegalArgumentException("递增因子必须大于0");
        }
        Long result = redisTemplate.opsForValue().increment(getKey(key), delta);
        this.setExpire(key, expire);
        closeConnection();
        return result;
    }

    /**
     * 递减
     *
     * @param key 键
     * @param delta  要减少几(小于0)
     * @return
     */
    @Override
    public Long decrease(String key, long delta) {
        if (delta < 0) {
            throw new IllegalArgumentException("递减因子必须大于0");
        }
        Long result = redisTemplate.opsForValue().increment(getKey(key), -delta);
        closeConnection();
        return result;
    }

    // ================================Map=================================

    /**
     * HashGet
     *
     * @param key  键 不能为null
     * @param item 项 不能为null
     * @return 值
     */
    @Override
    public Object hashGet(String key, String item) {
        Object obj = redisTemplate.opsForHash().get(getKey(key), item);
        closeConnection();
        return obj;
    }

    /**
     * 获取变量中的键值对
     *
     * @param key 键
     * @return 对应的多个键值
     */
    @Override
    public Map<Object, Object> hashGet(String key) {
        Map<Object, Object> entries = redisTemplate.opsForHash().entries(getKey(key));
        closeConnection();
        return entries;
    }

    /**
     * HashSet 以map集合的形式添加键值对
     *
     * @param key 键
     * @param map 对应多个键值
     * @return true 成功 false 失败
     */
    @Override
    public boolean hashSet(String key, Map<String, Object> map) {
        try {
            redisTemplate.opsForHash().putAll(getKey(key), map);
            return true;
        } catch (Exception e) {
            log.error("redis error: ", e);
            return false;
        } finally {
        	closeConnection();
        }
    }

    /**
     * HashSet 以map集合的形式添加键值对 并设置时间
     *
     * @param key  键
     * @param map  对应多个键值
     * @param time 时间(秒)
     * @return true成功 false失败
     */
    @Override
    public boolean hashSet(String key, Map<String, Object> map, long time) {
        try {
            redisTemplate.opsForHash().putAll(getKey(key), map);
            this.setExpire(key, time);
            return true;
        } catch (Exception e) {
            log.error("redis error: ", e);
            return false;
        } finally {
			closeConnection();
		}
    }

    /**
     * 向一张hash表中放入数据,如果不存在将创建
     *
     * @param key   键
     * @param item  项
     * @param value 值
     * @return true 成功 false失败
     */
    @Override
    public boolean hashSet(String key, String item, Object value) {
        try {
            redisTemplate.opsForHash().put(getKey(key), item, value);
            return true;
        } catch (Exception e) {
            log.error("redis error: ", e);
            return false;
        } finally {
			closeConnection();
		}
    }

    /**
     * 向一张hash表中放入数据,如果不存在将创建
     *
     * @param key   键
     * @param item  项
     * @param value 值
     * @param time  时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
     * @return true 成功 false失败
     */
    @Override
    public boolean hashSet(String key, String item, Object value, long time) {
        try {
            redisTemplate.opsForHash().put(getKey(key), item, value);
            this.setExpire(key, time);
            return true;
        } catch (Exception e) {
            log.error("redis error: ", e);
            return false;
        } finally {
			closeConnection();
		}
    }

    /**
     * 删除hash表中的值
     *
     * @param key  键 不能为null
     * @param item 项 可以使多个 不能为null
     * @return 
     */
    @Override
    public Long hashDel(String key, Object... item) {
        Long result = redisTemplate.opsForHash().delete(getKey(key), item);
        closeConnection();
        return result;
    }

    /**
     * 判断hash表中是否有该项的值
     *
     * @param key  键 不能为null
     * @param item 项 不能为null
     * @return true 存在 false不存在
     */
    @Override
    public Boolean hashHasKey(String key, String item) {
        Boolean result = redisTemplate.opsForHash().hasKey(getKey(key), item);
        closeConnection();
        return result;
    }

    /**
     * hash递增 如果不存在,就会创建一个 并把新增后的值返回
     * 变量中的键以double值的大小进行自增长
     * @param key  键
     * @param item 项
     * @param by   要增加几(大于0)
     * @return
     */
    @Override
    public Double hashIncrement(String key, String item, double by) {
        Double result = redisTemplate.opsForHash().increment(getKey(key), item, by);
        closeConnection();
        return result;
    }

    /**
     * hash递减
     *
     * @param key  键
     * @param item 项
     * @param by   要减少记(小于0)
     * @return
     */
    @Override
    public Double hashDecrease(String key, String item, double by) {
        Double result = redisTemplate.opsForHash().increment(getKey(key), item, -by);
        closeConnection();
        return result;
    }
    
    /**
     * 获取指定变量中的hashMap值
     *
     * @param key  键
     * @return List<Object>
     */
    @Override
    public List<Object> hashValues(String key) {
		List<Object> values = redisTemplate.opsForHash().values(getKey(key));
		closeConnection();
		return values;
	}
    
    /**
     * 获取指定变量中的hashMap的键
     *
     * @param key  键
     * @return Set<Object>
     */
    @Override
    public Set<Object> hashKeys(String key) {
		Set<Object> keys = redisTemplate.opsForHash().keys(getKey(key));
		closeConnection();
		return keys;
	}

    // ============================set=============================

    /**
     * 根据key获取Set中的所有值
     *
     * @param key 键
     * @return Set<Object>
     */
    @Override
    public Set<Object> setGet(String key) {
    	Set<Object> members = redisTemplate.opsForSet().members(getKey(key));
        closeConnection();
        return members;
    }

    /**
     * 根据value从一个set中查询,是否存在
     *
     * @param key   键
     * @param value 值
     * @return true 存在 false不存在
     */
    @Override
    public Boolean setHasKey(String key, Object value) {
    	Boolean result = redisTemplate.opsForSet().isMember(getKey(key), value);
    	closeConnection();
    	return result;
    }

    /**
     * 将数据放入set缓存
     *
     * @param key    键
     * @param values 值 可以是多个
     * @return 成功个数
     */
    @Override
    public Long setSet(String key, Object... values) {
    	Long result = redisTemplate.opsForSet().add(getKey(key), values);
    	closeConnection();
    	return result;
    }

    /**
     * 将set数据放入缓存
     *
     * @param key    键
     * @param time   时间(秒)
     * @param values 值 可以是多个
     * @return 成功个数
     */
    @Override
    public Long setSet(long time,String key, Object... values) {
    	Long count = redisTemplate.opsForSet().add(getKey(key), values);
        closeConnection();
    	setExpire(key, time);
    	return count;
    }

    /**
     * 获取set变量中值的长度
     *
     * @param key 键
     * @return
     */
    @Override
    public Long setSize(String key) {
    	Long result = redisTemplate.opsForSet().size(getKey(key));
    	closeConnection();
    	return result;
    }

    /**
     * 移除值为value的元素
     *
     * @param key    键
     * @param values 值 可以是多个
     * @return 移除的个数
     */
    @Override
    public Long setRemove(String key, Object... values) {
    	Long count = redisTemplate.opsForSet().remove(getKey(key), values);
    	closeConnection();
    	return count;
    }
    
    // ===============================list=================================

    /**
     * 获取list缓存的内容
     *
     * @param key   键
     * @param start 开始
     * @param end   结束 0 到 -1代表所有值
     * @return
     */
    @Override
    public List<Object> listGet(String key, long start, long end) {
        try {
            return redisTemplate.opsForList().range(getKey(key), start, end);
        } catch (Exception e) {
            log.error("redis error: ", e);
            return null;
        } finally {
        	closeConnection();
        }
    }

    /**
     * 获取list缓存的长度
     *
     * @param key 键
     * @return
     */
    @Override
    public long listSize(String key) {
        try {
            return redisTemplate.opsForList().size(getKey(key));
        } catch (Exception e) {
            log.error("redis error: ", e);
            return 0;
        } finally {
        	closeConnection();
        }
    }

    /**
     * 通过索引 获取list中的值
     *
     * @param key   键
     * @param index 索引 index>=0时， 0 表头，1 第二个元素，依次类推；index<0时，-1，表尾，-2倒数第二个元素，依次类推
     * @return
     */
    @Override
    public Object listGet(String key, long index) {
        try {
            return redisTemplate.opsForList().index(getKey(key), index);
        } catch (Exception e) {
            log.error("redis error: ", e);
            return null;
        } finally {
        	closeConnection();
        }
    }

    /**
     * 将list放入缓存
     *
     * @param key   键
     * @param value 值
     * @return
     */
    @Override
    public Long listSet(String key, Object value) {
        try {
            return redisTemplate.opsForList().rightPush(getKey(key), value);
        } catch (Exception e) {
            log.error("redis error: ", e);
            return 0L;
        } finally {
        	closeConnection();
        }
    }

    /**
     * 将list放入缓存
     *
     * @param key   键
     * @param value 值
     * @param time  时间(秒)
     * @return
     */
    @Override
    public Long listSet(String key, Object value, long time) {
        try {
            Long result = redisTemplate.opsForList().rightPush(getKey(key), value);
            this.setExpire(key, time);
            return result;
        } catch (Exception e) {
            log.error("redis error: ", e);
            return 0L;
        } finally {
        	closeConnection();
        }
    }

    /**
     * 将list放入缓存
     *
     * @param key   键
     * @param value 值
     * @return
     */
    @Override
    public Long listSet(String key, List<Object> value) {
        try {
            return redisTemplate.opsForList().rightPushAll(getKey(key), value);
        } catch (Exception e) {
            log.error("redis error: ", e);
            return 0L;
        } finally {
        	closeConnection();
        }
    }

    /**
     * 将list放入缓存
     *
     * @param key   键
     * @param value 值
     * @param time  时间(秒)
     * @return
     */
    @Override
    public Long listSet(String key, List<Object> value, long time) {
        try {
            Long result = redisTemplate.opsForList().rightPushAll(getKey(key), value);
            this.setExpire(key, time);
            return result;
        } catch (Exception e) {
            log.error("redis error: ", e);
            return 0L;
        } finally {
			closeConnection();
		}
    }

    /**
     * 在集合的指定位置插入元素,如果指定位置已有元素，则覆盖，没有则新增，超过集合下标+n则会报错
     * @param key   键
     * @param index 索引
     * @param value 值
     * @return
     */
    @Override
    public boolean listSet(String key, long index, Object value) {
        try {
            redisTemplate.opsForList().set(getKey(key), index, value);
            return true;
        } catch (Exception e) {
            log.error("redis error: ", e);
            return false;
        }finally {
        	closeConnection();
		}
    }

    /**
     * 移除N个值为value
     *
     * @param key   键
     * @param count 移除多少个
     * @param value 值
     * @return 移除的个数
     */
    @Override
    public long lRemove(String key, long count, Object value) {
        try {
            return redisTemplate.opsForList().remove(getKey(key), count, value);
        } catch (Exception e) {
            log.error("redis error: ", e);
            return 0;
        } finally {
        	closeConnection();
        }
    }

    // ===============================sorted set=================================

    /**
     * 向有序集合添加一个成员的
     *
     * ZADD key score1 member1 [score2 member2]
     *
     */
    @Override
    public boolean zadd(String key, Object member, double score, long time) {
    	Boolean result = redisTemplate.opsForZSet().add(getKey(key), member, score);
    	closeConnection();
    	setExpire(key, time);
    	return result;
    }

    /**
     *     ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT]
        通过分数返回有序集合指定区间内的成员
     *
     */
    @Override
    public Set<Object> zRangeByScore(String key, double minScore, double maxScore) {
        try {
            return redisTemplate.opsForZSet().rangeByScore(getKey(key), minScore, maxScore);
        } catch (Exception e) {
            log.error("redis error: ", e);
            return null;
        } finally {
			closeConnection();
		}
    }

    /**
     *  ZSCORE key member
     *  返回有序集中，成员的分数值
     *
     */
    @Override
    public Double zscore(String key, Object member) {
        try {
            return redisTemplate.opsForZSet().score(getKey(key), member);
        } catch (Exception e) {
            log.error("redis error: ", e);
            return null;
        } finally {
			closeConnection();
		}
    }

    /**
     *     ZRANK key member 返回有序集合中指定成员的索引
     *
     */
    @Override
    public Long zrank(String key, Object member) {
        try {
            Long rank = redisTemplate.opsForZSet().rank(getKey(key), member);
            return rank;
        } catch (Exception e) {
            log.error("redis error: ", e);
            return 0L;
        } finally {
			closeConnection();
		}
    }

    /**
     * Zscan 迭代有序集合中的元素（包括元素成员和元素分值）
     *
     */
    @Override
    public Cursor<ZSetOperations.TypedTuple<Object>> zscan(String key) {
        try {
            Cursor<ZSetOperations.TypedTuple<Object>> cursor = redisTemplate.opsForZSet().scan(getKey(key), ScanOptions.NONE);
            return cursor;
        } catch (Exception e) {
            log.error("redis error: ", e);
            return null;
        } finally {
        	closeConnection();
		}
    }
    
    /**
     * 设置key失效时间
     * 
     * @return void
     */
	private void setExpire(String key,Long time) {
		if (time > 0) {
            this.expire(key, time);
        }else {
        	log.warn("time 设置无效{}", time);
        }
	}
    
    /**
     * 释放连接
     * 
     * @return void
     */
    private void closeConnection() {
    	RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory());
    }

    /**
     * 获取key
     * 
     * @param key
     * @return String
     */
    private String getKey(String key) {
    	return String.format("%s:%s", redisProperties.getRedisPrefix(), key);
    }
}
